iT邦幫忙

2024 iThome 鐵人賽

0
Kubernetes

一起來看 Kubernetes 官方文件吧!系列 第 18

Day18 - 一起來看 Kubernetes 官方文件吧!- Node-pressure Eviction

  • 分享至 

  • xImage
  •  

前言

昨天我們看了 node 上面的各種 status 描述,而這些 status 當不健康時又會如何呢?

今日目標

  • 了解何時會觸發 Node-pressure Eciction

Node-pressure Eviction

kubelet 會收集各種 nodes 的資訊,並且依據配置的閥值啟動 Eviction(驅逐) 釋放資源

因為 node pressure 而導致的驅逐,kubelet 會把 pods 設定為 Failed 的狀態,並且關閉pods

Node-pressure 與 API 所觸發的驅逐是不同的 (e.g. drain node),其中最大的差異就是 node-pressure 驅逐並 “不一定” 會遵循 terminationGracePeriodSeconds 的秒數,兩種情況:

  • 觸發的是 soft eviction thresholds,kubelet 會遵循 eviction-max-pod-grace-period 配置的秒數
  • 觸發的是 hard eviction thresholds,則 grace period 會被強制設定為 0s (意即強制刪除)

Self healing behavior

當資源不足時,kubelet 會嘗試先獲取 node-level 的資源,然後才會嘗試刪除使用者建立的 pods,例如當 disk 空間不足時,會嘗試刪除 node 上未使用到的 container images
而若是 pods 有由 controller 管理 (e.g. deployment) 的話,controller-manger 會嘗試建立新的 pods 以取代掉被驅逐的 pods

Self healing for static pods

因為 static pods 就是會強制放在該 node 上,因此 kubelet 會嘗試重建,但 kubelet 依然會嘗試讓所有的 static pods 能夠正確運作。

Eviction signals and thresholds

關於 Eviction 的配置,kubelet 有三個:

  • Eviction signals:觀察資源的對象
  • Eviction thresholds:當資源對象的限制值觸發時,就可能會觸發
  • Monitoring intervals:預設為 10s 掃一次 (應該就是指 nodeStatusUpdateFrequency 參數)

Eviction signals

Eviction signals

在節點的硬體資源上,大致上可以分三大類:

  1. 記憶體相關:memory.availble
  2. 硬碟空間相關:{nodefs, imagefs, containerfs}.{avilable,inodesFree}
  3. process 相關:pid.available

註:contiainerfs 是相對較新的功能,在目前的 k8s 1.31 版本中必須開啟 KubeletSeparateDiskGC 功能才能使用,且目前僅能用於 CRI-O 的 container runtime 上

Memory signals

在 linux 的 node 上,memory.available 是由 cgroupfs 衍伸出的,這個數值會與 free -m 輸出的結果有點差異
官方文件提供了以下 script 模擬 kubelet 查詢 cgroupv2 的記憶體計算方式:

#!/bin/bash

# This script reproduces what the kubelet does
# to calculate memory.available relative to kubepods cgroup.

# current memory usage
memory_capacity_in_kb=$(cat /proc/meminfo | grep MemTotal | awk '{print $2}')
memory_capacity_in_bytes=$((memory_capacity_in_kb * 1024))
memory_usage_in_bytes=$(cat /sys/fs/cgroup/kubepods.slice/memory.current)
memory_total_inactive_file=$(cat /sys/fs/cgroup/kubepods.slice/memory.stat | grep inactive_file | awk '{print $2}')

memory_working_set=${memory_usage_in_bytes}
if [ "$memory_working_set" -lt "$memory_total_inactive_file" ];
then
    memory_working_set=0
else
    memory_working_set=$((memory_usage_in_bytes - memory_total_inactive_file))
fi

memory_available_in_bytes=$((memory_capacity_in_bytes - memory_working_set))
memory_available_in_kb=$((memory_available_in_bytes / 1024))
memory_available_in_mb=$((memory_available_in_kb / 1024))

echo "memory.capacity_in_bytes $memory_capacity_in_bytes"
echo "memory.usage_in_bytes $memory_usage_in_bytes"
echo "memory.total_inactive_file $memory_total_inactive_file"
echo "memory.working_set $memory_working_set"
echo "memory.available_in_bytes $memory_available_in_bytes"
echo "memory.available_in_kb $memory_available_in_kb"
echo "memory.available_in_mb $memory_available_in_mb"

在我的 master1 機器上,執行結果如下:

memory.capacity_in_bytes 4056092672
memory.usage_in_bytes 1837711360
memory.total_inactive_file 434024448
memory.working_set 1403686912
memory.available_in_bytes 2652405760
memory.available_in_kb 2590240
memory.available_in_mb 2529

Filesystem signals

  1. nodefs:node 主要的儲存空間,用於 local disk volume, emptyDir, logs, ephemeral storage 以及更多
  2. imagefs:container runtimes 用來儲存 container image 與 container 可寫層
  3. containerfs:與 nodefs 類似,但是用於 container 寫入的資訊。
    當 containerfs 啟用時,imagefs 僅存放 container images (唯讀層)

雖然上述分成三種,不過如果沒有特別切割系統空間的話,也可能會被看做同一份空間:

  • 所有都在 nodefs 內,也就是 OS 上僅切割一個 “root”
  • container storage 有另外的空間 (e.g. /var/lib/containerd 有另外切出) ,且 imagefs 於 root filesystem 切開,又被稱作 “split disk”

kubelet 將會自動檢測檔案系統的情況,且忽略其餘的 node filesystems,因此就算其他的空間達到上限,也不會觸發 “Filesystem signals”

Eviction thresholds

設定觸發上限分成 sort 與 hard,配置上的格式為:[eviction-signal][operator][quantity]

  • eviction-signal:就是上面的那些
  • operator (預算符):大於小於等等
  • quantity (數量):可以配置整數 (e.g. 1Gi) 或百分比 (%),但只能擇一

Soft eviction thresholds

Soft 顧名思義,就是比較軟性的限制,通常會比起 Hard 不那麼嚴苛
當觸發 Soft 上限時,被驅逐的 pods 會以 graceful 的方式被關閉

透過以下配置來設定 soft eviction thresholds:

  • eviction-soft:配置 thresholds,比如: memory.available <1.5Gi
  • eviction-soft-grace-period:當觸發對應條件時,會等多久才開始動作,如 memory.available=1m30s 代表該狀態持續 1分30秒 後才會觸發
  • eviction-max-pod-grace-period:當觸發驅逐的 pods 最多可以忍受關閉多久時間,超過之後就會強制刪除了

Hard eviction thresholds

觸發後就沒有 grace period 的條件,會立刻的把 pods 刪除 (官方文件這邊寫為了獲取 “飢餓的資源”)

因為不用配置 grace period 的關係,hard eviction 僅有一個參數為:eviction-hard,直接配置 hard eviction threshold (如 memory.available<1Gi )

以下是一些預設的條件:

  • memory.available<100Mi (Linux nodes)
  • memory.available<500Mi (Windows nodes)
  • nodefs.available<10%
  • imagefs.available<15%
  • nodefs.inodesFree<5% (Linux nodes)
  • imagefs.inodesFree<5% (Linux nodes)

要特別注意的是,僅有完全沒配置的情況才會套用預設值,若其中之一有配置的話,則其他未配置的參數會被改為 0 → 因此要不就是完全沒配置,不然就是全部都要記得配置!!

containerfs 比較特別,她的 thresholds 會依據磁碟切割分為:

  • 當僅有 root filesystem:則 containerfsnodefs 一樣
  • 當有切割 filesystem 時:containerfsimagefs 一樣

Node conditions

跟昨日看的 condition 是一樣的,基本上就是相對應的資源:

  • MemoryPressure:memory.available
  • DiskPressure:其他所有磁碟相關的
  • PIDPressure:pid.available

kubelet 預設每 10s 會更新這些狀態

Node condition oscillation (節點狀態震盪)

Node condition oscillation 指的是 soft eviction thresholds 的狀態不斷來回,可能會導致錯誤的 eviction 情況

可以配置 eviction-pressure-transition-period參數,讓 kubelet 必須等待一段時間後,才會開始進行 sort eviction,預設為 5m

Reclaiming node level resources

在驅逐 pods 之前,kubelet 會先嘗試從 node-level 去獲取資源 (e.g DiskPresure)

  • 當僅配置 nodefs 時,kubelet 會嘗試:
    • 將已經為 dead 狀態的 pods 與 containers 刪除
    • 刪除所有未使用的 images
  • 有配置 imagefs:也就是有特別切出空間給 container runtimes 使用的情況下
    • 若 nodefs 觸發時,kubelet 將已經為 dead 狀態的 pods 與 containers 刪除
    • 若 imagefs 觸發 thresholds 時,kubelet 會刪除所有未使用的 images

Pod selection for kubelet eviction

當 Node-level 的資源都無法滿足資源需求時,就會開始嘗試刪除由 user 建立的 pods,順序為:

  1. pod 資源使用是否超過其 requests
  2. Pod Priority:根據 pods 所配置的優先度
  3. pods 的資源使用與其 request 的相對關係

kubelet 會針對 pods 定義一些等級:

  • BestEffort or Burstable 且實際使用量大於 request 的 pods 會優先被驅逐。
  • Guaranteed pods and Burstable pods 且實際使用量低於 request 的將會最後被考慮驅逐。

但若是 EphemeralStorage (DiskPressure) 的情況則上述的優先順序無效

若希望 static pods 不要受到驅逐影響,則可以配置 priority 欄位 (priorityClassName 欄位在 static pod 沒作用)

可以看看 apiserver 的 yaml,也有配置 priority 欄位:

❯ cat /etc/kubernetes/manifests/kube-apiserver.yaml
  priority: 2000001000
  priorityClassName: system-node-critical

Minimum eviction reclaim

Minimum eviction reclaim 是設定一定的值,使得每次觸發 eviction 時,獲取足夠的資源,而非超過 threshold 而已,範例配置如下:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
evictionHard:
  memory.available: "500Mi"
  nodefs.available: "1Gi"
  imagefs.available: "100Gi"
evictionMinimumReclaim:
  memory.available: "0Mi"
  nodefs.available: "500Mi"
  imagefs.available: "2Gi"

nodefs 觸發時,代表總空間 < 1Gi,此時會最少一次獲取 500Mi 的空間,使得空間至少有 1.5Gi (1Gi + 500Mi)
imagefs 的範例,當觸發時代表 imagefs < 100Gi,kubelet 會嘗試獲取 2Gi 的空間,使得空間至少有 102Gi
但當 kubelet 發現獲取資源無法達到 minimum 的情況時,則不會做任何動作

該值對所有資源的預設為 0

Good practices

Schedulable resources and eviction policies

當以下情況時:

  • Node memory 總共有:10GiB
  • 希望保留 10% 的 memory 給 system daemons (e.g. kubelet, kernel, etc.)
  • 希望在 memory 使用率達 95% 觸發驅逐,以避免造成系統的 OOM
--eviction-hard=memory.available<500Mi
--system-reserved=memory=1.5Gi

以上配置會預留 1.5G 的 memory 給系統使用,並且當 pods 占用 memory 過多導致剩餘 500 MiB 時觸發驅逐。

測試 --system-reserved 參數

因為 kubelet 目前都盡量要使用 config 的方式來帶入參數,因此若要測試的話,可以調整 /var/lib/kubelet/config.yaml

systemReserved:
  memory: 1G
❯ sudo systemctl restart kubelet

改完之後會發現使用 kubectl describe 顯示 memory 可分配的值變少了:

❯ k describe no k8s-master1  | grep memory
# 改之前,上面是總空間,下面是可分配空間
  memory:             3961028Ki
  memory:             3858628Ki
# 改之後
  memory:             3961028Ki
  memory:             2951235072

這個配置目前只能配置 CPU 與 memory 兩種資源。

DaemonSets and node-pressure eviction

因為某些 DaemonSet 是重要的服務 (e.g. CNI),因此可能不會希望這些 pods 能不要那麼容易被驅逐
方法就如同上面討論的,將 pods 的 priority 調整高一點,就可以降低 damonSet 的 pods 被驅逐的機會
反言之,若 DaemonSet 的 Pods 並沒有那麼重要,則可以把 priority 調低一點或者用預設的,就可以優先把資源給其他 pods 使用

預設情況下,kube-proxy 即是以 daemonset 的方式來跑的,也可以發現有配置 priorityClassName: system-node-critical,使得 kube-proxy 有著高優先度。

❯ k get ds -n kube-system kube-proxy -o yaml | grep priority
...
      priorityClassName: system-node-critical

結論

看完這篇之後,就對 kubelet 的 GC 機制比較了解了一些,搭配上昨天的 node status,就可以比較理解當資源吃緊時,kubelet 會以多少的頻率發現問題並處理等等等

針對一些比較重要的服務也可以透過配置 priority 使其高於一般的 pods,就可以存活比較久一點啦 ~

參考

https://kubernetes.io/docs/concepts/scheduling-eviction/node-pressure-eviction/

https://kubernetes.io/docs/reference/config-api/kubelet-config.v1beta1/


上一篇
Day17 - 一起來看 Kubernetes 官方文件吧!- Node status
下一篇
Day19 - 一起來看 Kubernetes 官方文件吧!- pods request/limit (QoS)
系列文
一起來看 Kubernetes 官方文件吧!19
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言